package org.xqh.study.google.guava.hash;

import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import org.junit.Test;

import java.nio.charset.Charset;

/**
 * Hash 散列
 * 1、Hashing
 * 2、BloomFilter
 */
public class HashTest {
    //Guava Hash(散列)指的是通过某种算法把数据源通过一系列的转换生成一串字符串。
    // 常见的例如hash code生成，加密字符的生成，检验字符的生成等等。

    //Hash里面比较重要的类有：
    // Hashing：Hashing类是一个帮助类，里面提供的方法都是根据不同的hash算法生成对应的HashFunction对象。每个hash算法都实现了HashFunction。
    // HashFunction： HashFunction有两个作用：创建Hasher对象、也可以直接根据输入条件返回HashCode结果。
    // Hasher：Hasher的主要作用是根据加入的数据源得到HashCode。数据源通过提供的putXxx（）方法添加、com.guava.test.hash()方法返回计算结果HashCode。
    // HashCode：HashCode是对结果的一个封装。
    // Funnel、PrimitiveSink： 定义了怎样将一个Object对象里面的各个属性放到PrimitiveSink里面，Hasher的putObject方法需要传入这样的对象。
    @Test
    public void testHashing() {
        //md5 加密算法
        System.out.println(Hashing.md5().hashString("abc", Charsets.UTF_8));//900150983cd24fb0d6963f7d28e17f72

        //返回一个多用途的，临时使用的，非加密的 Hash Function
        System.out.println(Hashing.goodFastHash(7));//Hashing.murmur3_32(-1250502676)

        //sha256 加密算法
        System.out.println(Hashing.sha256().hashString("abc", Charsets.UTF_8));//ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

        //sha512 加密算法
        System.out.println(Hashing.sha512().hashString("abc", Charsets.UTF_8));//ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f

        //一致性hash 。bucket 的范围在 0 ~ buckets 之间
        //传入数据主键id(分片片键)和集群中机器数量buckets，返回一个固定的数字，表示数据应当落在第几个机器上。
        System.out.println(Hashing.consistentHash(5678787, 100)); //22

    }

    //布隆过滤器。
    //布隆过滤器是一种空间利用率较高的概率型数据结构，用来测试一个元素是否在集合中。但是存在一定可能，导致结果误判。即元素不在集合中，查询结果却返回元素在集合中。
    @Test
    public void testBloomFilter() {
        //创建布隆过滤器时要传入期望处理的元素数量，及最期望的误报的概率。如下分别是1000和0.000001。
        //因为布隆过滤器是一种概率型数据结构，因此返回true表示元素有极大的概率存在。当返回false那么表示元素一定不存在。
        //当我们创建布隆过滤器时，尽可能提供准确的元素数量。否则可能会产生较高的误报率。
        BloomFilter<String> b = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000, 0.000001);
        b.put("1");
        b.put("2");
        b.put("3");
        System.out.println(b.mightContain("6"));

        BloomFilter<String> b1 = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000, 0.000001);
        b1.put("4");
        b1.put("5");
        b1.put("6");
        b1.putAll(b);
        System.out.println(b1.mightContain("6"));

        /*//这里想试试put进去的数据量过大，会不会出现误报的现象。据说数据量过亿级的时候，误报率会相对变高
        //结果没有试出来，看来误报概率还挺小
        IntStream.rangeClosed(0, 200000000).mapToObj(String::valueOf).forEach(b1::put);

        IntStream.rangeClosed(0, 100).forEach(a -> {
            Boolean exist = b1.mightContain("10000");
            System.out.println(exist);
            if(!exist) {
                return;
            }
        });*/

    }
}
